/**
 * atWakaka.js
 * @author zhangxinxu(.com)
 * @created 2022-08-25
 * @description 在div富文本中实现 AT 功能，依赖于 tributejs （https://github.com/zurb/tribute）
 *              为何文件名有个奇怪的'Wakaka'，我也不知道为什么，就有强烈的想要加点奇怪名称的冲动
 *              不兼容 IE 浏览器
**/

import Tribute from "./tribute.esm.js";

const atWakaka = function (container, options, optionsTribute) {
    if (typeof container == 'string') {
        // 如果是选择器
        if (/^(?:#|\.)\w+/.test(container)) {
            container = document.querySelector('container');
        } else {
            // 否则当 ID 处理了
            container = document.getElementById('container');
        }
    }

    if (!container || !container.tagName) {
        return;
    }

    // 引入 tributejs
    if (!Tribute) {
        return;
    }

    // 常规参数
    var defaults = {
        url: '',
        pressEnter: null,
        // 鼠标经过的提示元素
        popOver: 'auto',
        // 鼠标经过和移出 @ 元素的处理
        onMouseOver: function () {},
        onMouseOut: function () {}
    };

    var params = Object.assign({}, defaults, options || {}); 

    // 如果有 URL，那么 values 值走请求
    let valuesTribute = [];

    if (params.url) {
        valuesTribute = function (key, callback) {
            let url = params.url;
            if (typeof url == 'function') {
                url = url(key);
            } else if (typeof url == 'string') {
                url = url.replace('${key}', key);
            }

            // 请求
            fetch(url).then(res => res.json()).then(json => {
                if (json && json.data) {
                    const dataHandle = json.data.map(obj => {
                        obj.value = obj.userid;
                        obj.key = obj.username;
                        return obj;
                    });
                    // 数据暴露在外
                    params.data = dataHandle;
                    // 给 tribute 使用
                    callback(dataHandle);
                }
            });
        }
    }


    // 默认参数
    var defaultsTribute = {
        containerClass: 'ui-at-drop-x',
        // 前面不需要有空格
        requireLeadingSpace: false,
        searchOpts: {
            pre: '<mark>',
            post: '</mark>',
            // 是否服务端搜索数据
            skip: true
        },
        // 动态获取匹配的值
        values: valuesTribute,
        
        noMatchTemplate: function () {
            return '<li class="ui-at-empty">没有匹配的人</li>';
        },
        selectTemplate: function(item) {
            let origin = item.original;
            // 塞入 atEmp 数据
            let { element } = tribute.current;
        
            if (!element) {
                return '';
            }
        
            return `<hr class="ui-at-at" data-name="${origin.key}" data-id="${origin.value}" is-align="false">`;
        },
        menuItemTemplate: function (item) {
            let origin = item.original;
            let { mentionText } = tribute.current;
        
            let strName = item.string;
        
            // 高亮的处理
            if (mentionText) {
                strName = strName.replaceAll(mentionText, '<mark>' + mentionText + '</mark>');
            }
        
            return `<a href="javascript:" class="ui-at-li">
                <img class="ui-at-avatar" src="${origin.avatar}" alt="${origin.value}">
                <div class="ui-at-info">
                    <span class="ui-at-name">${strName}</span>
                    <div class="ui-at-department">
                    ${origin.department}
                    </div>
                </div>
            </a>`;
        }
    };


    // @ 提示功能的实现
    let paramsTribute = Object.assign({}, defaultsTribute, optionsTribute || {});
    const tribute = new Tribute(paramsTribute);
    tribute.attach(container);

    // 一些事件处理
    // 1. 粘贴和拖拽进来时候的 HTML 过滤
    const doStripHtml = function (event) {
        var dataInput = event.clipboardData || event.dataTransfer;
        if (!dataInput || !dataInput.getData) {
            // IE 直接禁止粘贴和拖拽
            event.preventDefault();
            return;
        }

        var htmlOrigin = dataInput.getData('text/html');
        let textOrigin = dataInput.getData('text');

        // 如果包含富文本
        if (htmlOrigin) {
            // 手动插入
            // 阻止默认的行为
            event.preventDefault();

            // 只插入纯文本
            let lastRange = window.getSelection().getRangeAt(0);
            const newNode = document.createTextNode(textOrigin);
            lastRange.deleteContents();
            lastRange.insertNode(newNode);
            lastRange.setStartAfter(newNode);
            event.target.focus();
        }

        // 兜底处理，删除所有 <hr> 标签以外的元素
        setTimeout(() => {
            [...event.target.children].forEach(ele => {
            if (ele.matches('hr') == false) {
                ele.remove();
            }
            });

            event.target.dispatchEvent(new CustomEvent('input'));
        }, 20);
    };

    container.addEventListener('paste', doStripHtml);
    container.addEventListener('drop', doStripHtml);

    // 回车的处理
    if (typeof params.pressEnter == 'function') {
        container.addEventListener('keydown', event => {
            if (event.keyCode === 13) {
                params.pressEnter(event);
            }
        });
    }

    var elePopOver = params.popOver;
    if (elePopOver == 'auto' || !elePopOver) {
        elePopOver = document.createElement('div');
        elePopOver.className = 'ui-at-popover';
        document.body.append(elePopOver);
    }

    var timerPopOverHide = null;

    // 鼠标经过的处理
    container.addEventListener('mouseover', function (event) {
        let eleTarget = event.target;

        if (eleTarget && eleTarget.tagName == 'HR') {
            let id = eleTarget.dataset.id;

            if (!id) {
                return;
            }

            event.container = this;
            event.popover = elePopOver;

            let boundTarget = eleTarget.getBoundingClientRect();

            // 清除隐藏的定时器
            clearTimeout(timerPopOverHide);

            // 显示并定位
            elePopOver.toggleAttribute('open', true);
            elePopOver.style.left = (boundTarget.left + boundTarget.width / 2 - elePopOver.clientWidth / 2) + 'px';
            elePopOver.style.top = (boundTarget.bottom + 5) + 'px';

            // 对外暴露的事件处理
            let dataMatch = params.data.filter(obj => obj.value == id)[0];
            // 例如可以根据传递的数据进行人物信息请求
            params.onMouseOver(event, dataMatch, elePopOver); 
        }
    });

    container.addEventListener('mouseout', function (event) {
        let eleTarget = event.target;

        if (eleTarget && eleTarget.tagName == 'HR') {
            let id = eleTarget.dataset.id;

            if (!id) {
                return;
            }

            // 同样的，通过 event 对象将可能需要的元素暴露出去
            event.container = this;
            event.popover = elePopOver;

            // 浮层隐藏定时器启动
            timerPopOverHide = setTimeout(() => {
                elePopOver.toggleAttribute('open', false);
            }, 150);

            let dataMatch = params.data.filter(obj => obj.value == id)[0];
            
            // 对外暴露的回调处理
            params.onMouseOut(event, dataMatch, elePopOver); 
        }
    });

    // 鼠标经过 popover 元素也要阻止隐藏 
    elePopOver.addEventListener('mouseenter', function () {
        // 清除隐藏的定时器
        clearTimeout(timerPopOverHide);
    });
    elePopOver.addEventListener('mouseleave', function () {
        // 开启隐藏的定时器
        timerPopOverHide = setTimeout(() => {
            elePopOver.toggleAttribute('open', false);
        }, 150);
    });

    return tribute;
};

export default atWakaka;